package com.itheima.core;

import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @作者 itcast
 * @创建日期 2020/10/13 15:05
 **/
public class DefaultListableBeanFactory implements BeanFactory{
    // 存储bean的定义信息  key: bean的id     value: bean的描述
    private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    // 存到单例对象的map集合  key: bean的id    value: 单例对象
    private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>();

    public void initSingletonObjects(){
        beanDefinitionMap.forEach((key,val)->{
            String scope = val.getScope();
            if("singleton".equals(scope)){
                getBean(key);
            }
        });
    }


    /**
     * 获取对象方法实现
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        // 1. 先去单例map集合中 查询是否有要找的对象
        Object singletonObj = this.singletonObjects.get(beanName);
        // 有直接返回  没有下一步
        if(singletonObj!=null){
            return singletonObj;
        }
        // 2. 查询beanDefinitionMap查看是否有该bean的定义
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
            // 没有抛出异常  有下一步
            throw new RuntimeException(" No Such BeanDefinition Exception");
        }
        // 3. 获取bean定义中的scope属性
        String scope = beanDefinition.getScope();
        if("singleton".equals(scope)){
            // singleton 单例
            synchronized (this.singletonObjects){
                Object obj = this.singletonObjects.get(beanName);
                if(obj == null){
                    // 使用双检索判断单例集合是否包含
                    // 调用createBean的方法创建对象
                    obj = createBean(beanDefinition);
                    // 将对象存入到单例集合
                    this.singletonObjects.put(beanName,obj);
                }
                return obj;
            }
        }else {
            // prototype 多例
            // 调用createBean的方法创建对象并返回
            return createBean(beanDefinition);
        }
    }

    /**
     * 创建对象的方法
     * @param beanDefinition
     * @return
     */
    private Object createBean(BeanDefinition beanDefinition){
        // 1. 根据bean定义 得到对应的Class对象  com.itheima.pojo.Teacher
        String className = beanDefinition.getClassName();
        Class<?> aClass;
        try {
            aClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("ClassNotFoundException 没找到类");
        }
        // 2. 查看是否配置了 factory-method 如果配置了工厂方法 反射调用工厂方法创建对象
        // 3. 查看是否配置了构造器参数, 匹配对应的有参构造器  反射创建对象
        // 4. 调用无参构造器 通过反射创建对象
        Object obj;
        try {
            obj = aClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException("InstantiationException 反射创建失败");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("IllegalAccessException 反射创建失败");
        }
        // 5. 根据属性列表进行依赖注入
        List<Property> propertiyList = beanDefinition.getPropertiyList();
        for (Property property : propertiyList) {
            String name = property.getName();
            String ref = property.getRef();
            String value = property.getValue();
            // BeanUtils
            try {
                if(name!=null && !"".equals(name)){
                    if(value!=null && !"".equals(value)){
                        // 1. 目标对象   2. 属性名称  3. 属性的值
                        BeanUtils.setProperty(obj,name,value);
                    }
                    // ref
                    if(ref!=null && !"".equals(ref)){
                        // 1. 目标对象   2. 属性名称  3. 属性的值
                        BeanUtils.setProperty(obj,name,getBean(ref));
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new RuntimeException("IllegalAccessException 反射创建失败");
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                throw new RuntimeException("IllegalAccessException 反射创建失败");
            }
        }
        return obj;
    }

    /**
     * bean的注册
     * @param beanDefinition
     */
    public void registryBeanDefinition(BeanDefinition beanDefinition){
        this.beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition);
    }
}
